//###############
//FILE : RequestThread.java
//WRITER : Daniel Lowengrub, daniel_i_l, 320687098
//EXERCISE : oop ex2 2011
//DESCRIPTION:
//See below
//###############

package oop.ex3.communication;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;

/**
 * This is the base class for all threads that look for a request that matches
 * a given input stream and serves it if a match was found.
 * It's in charge of opening the input/output streams for the given socket and
 * closing them after the request is served. It's also in charge of sending an error
 * message if no appropriate request was found.
 * In addition, the request thread holds a dispatcher and it removes itself from that
 * dispatchers list when it's finished.
 * @author daniel
 */
public abstract class RequestThread extends Thread{
    private Socket _socket;
    private RequestThreadDispatcher _dispatcher;
    protected DataInputStream _inputStream;
    protected DataOutputStream _outputStream;
    protected ArrayList<Request> _requestList;

    /**
     * Create a request thread with a list of requests it should look for.
     * @param requestList The list of requests it should look for.
     */
    public RequestThread(ArrayList<Request> requestList) {
        _inputStream = null;
        _outputStream = null;
        _requestList = requestList;
    }

    /**
     * Set the request's dispatcher. This is the dispatcher that the thread will
     * remove itself from when it finishes.
     * @param dispatcher The dispatcher.
     */
    public void setDispatcher(RequestThreadDispatcher dispatcher) {
    	_dispatcher = dispatcher;
    }
    /**
     * Set the socket this thread should connect to.
     * @param socket The socket.
     */
    public void setSocket(Socket socket) {
        _socket = socket;
    }

    /**
     * Get a deep copy of this thread.
     * @return The deep copy.
     */
    public abstract RequestThread getCopy();

    /**
     * Try to open input/output stream on the socket specified with setSocket.
     * @return true on success and false on failure.
     */
    private boolean openStreams() {
        try {
            //first get the streams from the socket
            _inputStream = Communication.getInputStreamFromSocket(_socket);
            _outputStream = Communication.getOutputStreamFromSocket(_socket);
        } catch (IOException e) {
            //System.out.println("failed to open streams");
            closeSocket();
            return false; //there's nothing left to do
        }

        return true;
    }

    private void closeSocket() {
        try {
                _socket.close();
            } catch (IOException e) {
                //we just got the socket so we know it's valid
        }
    }

    private void closeSocketAndStreams() {
        try {
                _socket.close();
                _inputStream.close();
                _outputStream.close();

            } catch (IOException e) {
                //we just got the socket so we know it's valid
        }
    }

    @Override
    public void run(){
        if (!openStreams())
            return;

        //now look for the right server request
        for (Request request : _requestList) {
            if (request.matchHeader(_inputStream)) {
                request.getCopy().serveRequest(_inputStream, _outputStream);
                closeSocketAndStreams();
                return;
            }
        }

        //if we failed to find a request
        sendErrorMessage();

        closeSocketAndStreams();
        
        //remove ourself from the list
        _dispatcher.removeRequestFromPool(this);
        return;
    }

    /**
     * Send an error message if no request was found
     */
    protected abstract void sendErrorMessage();
}
